package com.qxczh.common.aspects;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qxczh.common.annotation.RequestLog;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.support.AopUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;


/**
 * @Describe //TODO
 * @ClassName RequestLogAspect
 * @Author Gaosl
 * @Date 2020/1/28 22:00
 */
@Component
@Aspect
@Slf4j
public class RequestLogAspect {

    /**
     * 错误信息
     */
    private static final String ERROR_MESSAGE = "打印入参方法异常";
    /**
     * 切点
     */
    @Pointcut("@annotation(com.qxczh.common.annotation.RequestLog)")
    public void requestLog() {
    }

    /**
     * 环绕切点
     *
     * @param point 切入点
     * @return Object
     */
    @Around("requestLog()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        // 获取注解
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method targetMethod = AopUtils.getMostSpecificMethod(methodSignature.getMethod(), point.getTarget().getClass());
        RequestLog requestLog = targetMethod.getAnnotation(RequestLog.class);
        ApiOperation apiOperation = targetMethod.getAnnotation(ApiOperation.class);
        // 获取HttpServletRequest
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 方法参数 默认请求参数放第一个
        Object[] objects = point.getArgs();
        // 打印参数
        String paramStr = "";
        if (requestLog.isPrintIn()) {
            try {
                paramStr = getParamStr(request, objects);
            } catch (Exception e) {
                log.error(ERROR_MESSAGE, e);
                paramStr = ERROR_MESSAGE;
            }
        }
        // 计算接口耗时
        long startTime = System.currentTimeMillis();
        Object result = null;
        log.info("\n 前置打印入参 \n 接口地址：{} \n  请求入参：{}", request.getServletPath() , paramStr);
        try {
            result = point.proceed();
        } catch (Throwable e) {
            log.error("\n 发生异常：{}", e.toString());
            throw e;
        } finally {
            // 日志输出
            String description = requestLog.description().equals("")?apiOperation.value():requestLog.description();
            String servletPath = request.getServletPath();
            int time = Math.toIntExact(System.currentTimeMillis() - startTime);
            log.info("\n 模块：{} \n 方法描述：{} \n 请求IP：{} \n 接口地址：{} \n 请求方式：{} \n 请求入参：{} \n 请求出参： {} \n 执行时间: {} ",
                    "default", description, request.getRemoteAddr(), servletPath, request.getMethod(),
                    paramStr, result != null && requestLog.isPrintOut() ? JSONObject.toJSONString(result) : "", time);
        }
        return result;
    }

    /**
     * 获取接口参数
     *
     * @param request HttpServletRequest
     * @param objects 参数集合
     * @return 参数json字符串
     */
    private String getParamStr(HttpServletRequest request, Object[] objects) {
        String paramStr = "";
        if (RequestMethod.GET.toString().equals(request.getMethod())) {
            paramStr = JSON.toJSONString(request.getParameterMap());
        } else {
            if (objects.length > 0) {
                paramStr = JSON.toJSONString(objects[0]);
            }
        }
        return paramStr;
    }

}
